import Commander
import Foundation
import PeekabooCore

@available(macOS 14.0, *)
@MainActor
struct RunCommand: OutputFormattable {
    nonisolated(unsafe) static var commandDescription: CommandDescription {
        MainActorCommandDescription.describe {
            CommandDescription(
                commandName: "run",
                abstract: "Execute a Peekaboo automation script",
                showHelpOnEmptyInvocation: true
            )
        }
    }

    @Argument(help: "Path to the script file (.peekaboo.json)")
    var scriptPath: String

    @Option(help: "Save results to file instead of stdout")
    var output: String?

    @Flag(help: "Continue execution even if a step fails")
    var noFailFast = false
    @RuntimeStorage private var runtime: CommandRuntime?

    private var resolvedRuntime: CommandRuntime {
        guard let runtime else {
            preconditionFailure("CommandRuntime must be configured before accessing runtime resources")
        }
        return runtime
    }

    private var services: any PeekabooServiceProviding { self.resolvedRuntime.services }
    private var logger: Logger { self.resolvedRuntime.logger }
    var outputLogger: Logger { self.logger }
    private var configuration: CommandRuntime.Configuration { self.resolvedRuntime.configuration }
    var jsonOutput: Bool { self.configuration.jsonOutput }
    private var isVerbose: Bool { self.configuration.verbose }

    @MainActor
    mutating func run(using runtime: CommandRuntime) async throws {
        self.runtime = runtime
        let startTime = Date()

        do {
            let script = try await ProcessServiceBridge.loadScript(services: self.services, path: self.scriptPath)
            let results = try await ProcessServiceBridge.executeScript(
                services: self.services,
                script,
                failFast: !self.noFailFast,
                verbose: self.isVerbose
            )

            let output = ScriptExecutionResult(
                success: results.allSatisfy(\.success),
                scriptPath: self.scriptPath,
                description: script.description,
                totalSteps: script.steps.count,
                completedSteps: results.count { $0.success },
                failedSteps: results.count { !$0.success },
                executionTime: Date().timeIntervalSince(startTime),
                steps: results
            )

            if let outputPath = self.output {
                let data = try JSONEncoder().encode(output)
                try data.write(to: URL(fileURLWithPath: outputPath))
                if !self.jsonOutput {
                    print("✅ Script completed. Results saved to: \(outputPath)")
                }
            } else if self.jsonOutput {
                outputSuccessCodable(data: output, logger: self.outputLogger)
            } else {
                self.printSummary(output)
            }

            if !output.success {
                throw ExitCode.failure
            }
        } catch {
            if self.jsonOutput {
                outputError(message: error.localizedDescription, code: .INVALID_ARGUMENT, logger: self.outputLogger)
            } else {
                print("❌ Error: \(error.localizedDescription)")
            }
            throw ExitCode.failure
        }
    }

    @MainActor
    private func printSummary(_ result: ScriptExecutionResult) {
        if result.success {
            print("✅ Script completed successfully")
        } else {
            print("❌ Script failed")
        }
        print("   Total steps: \(result.totalSteps)")
        print("   Completed: \(result.completedSteps)")
        print("   Failed: \(result.failedSteps)")
        print("   Execution time: \(String(format: "%.2f", result.executionTime))s")

        if !result.success {
            let failedSteps = result.steps.filter { !$0.success }
            if !failedSteps.isEmpty {
                print("\nFailed steps:")
                for step in failedSteps {
                    print("   - Step \(step.stepNumber) (\(step.command)): \(step.error ?? "Unknown error")")
                }
            }
        }
    }
}

struct ScriptExecutionResult: Codable {
    let success: Bool
    let scriptPath: String
    let description: String?
    let totalSteps: Int
    let completedSteps: Int
    let failedSteps: Int
    let executionTime: TimeInterval
    let steps: [PeekabooCore.StepResult]
}

private enum ProcessServiceBridge {
    static func loadScript(services: any PeekabooServiceProviding, path: String) async throws -> PeekabooScript {
        try await Task { @MainActor in
            try await services.process.loadScript(from: path)
        }.value
    }

    static func executeScript(
        services: any PeekabooServiceProviding,
        _ script: PeekabooScript,
        failFast: Bool,
        verbose: Bool
    ) async throws -> [StepResult] {
        try await Task { @MainActor in
            try await services.process.executeScript(script, failFast: failFast, verbose: verbose)
        }.value
    }
}

@MainActor
extension RunCommand: ParsableCommand {}
extension RunCommand: AsyncRuntimeCommand {}

@MainActor
extension RunCommand: CommanderBindableCommand {
    mutating func applyCommanderValues(_ values: CommanderBindableValues) throws {
        self.scriptPath = try values.decodePositional(0, label: "scriptPath")
        self.output = try values.decodeOption("output", as: String.self)
        self.noFailFast = values.flag("noFailFast")
    }
}
